/*-------------------------------------------------------------------------
 *
 * inet.h
 *	  对INET数据类型操作的声明。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/utils/inet.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef INET_H
#define INET_H

#include "fmgr.h"

/*
 * 这是 IP 地址的内部存储格式
 * （包括 INET 和 CIDR 数据类型）：
 */
typedef struct
{
	unsigned char family;		/* PGSQL_AF_INET 或 PGSQL_AF_INET6 */
	unsigned char bits;			/* 网络掩码中的位数 */
	unsigned char ipaddr[16];	/* 最多 128 位地址 */
} inet_struct;

/*
 * 我们使用这些值作为 "family" 字段。
 *
 * 将所有非 AF_INET 类型引用到 AF_INET 使我们能够在
 * 可能没有适当地址族的机器上工作（例如
 * 当 AF_INET6 不存在时的 inet6 地址），但不会导致
 * 转储/重新加载的需求。预 7.4 数据库在磁盘上使用 AF_INET 作为族
 * 类型。
 */
#define PGSQL_AF_INET	(AF_INET + 0)
#define PGSQL_AF_INET6	(AF_INET + 1)

/*
 * INET 和 CIDR 地址在 Postgres 中以 varlena
 * 对象的形式表示，即，在上面描绘的结构类型前有一个 varlena 头。
 * 该结构描绘了我们在 "未压缩" 情况下实际在内存中
 * 所拥有的内容。请注意，最大数据大小仅为
 * 18 字节，因此 INET/CIDR 总是以
 * 1 字节头的 varlena 格式存储到元组中。
 * 然而，我们必须做好应对
 * 4 字节头格式的准备，因为各种代码可能会有益地
 * 尝试 "解压缩" 1 字节头的值。
 */
typedef struct
{
	char		vl_len_[4];		/* 不要直接触摸这个字段！ */
	inet_struct inet_data;
} inet;

/*
 * 访问宏。我们使用 VARDATA_ANY，以便可以处理短头的
 * varlena 值，而无需解压它们。这需要一个技巧：
 * VARDATA_ANY 假设 varlena 头已经填充，而这并不是在构造新值时的情况
 * （在调用 SET_INET_VARSIZE 之前，通常我们不能直到最后）。
 * 因此，我们总是将新分配的值初始化为零（使用 palloc0）。
 * 一个零长度的字会看起来像 VARDATA_ANY 的非 1 字节情况，
 * 因此我们正确构造了一个未压缩的值。
 *
 * 注意，ip_addrsize()、ip_maxbits() 和 SET_INET_VARSIZE() 需要
 * 正确设置 family 字段。
 */
#define ip_family(inetptr) \
	(((inet_struct *) VARDATA_ANY(inetptr))->family)

#define ip_bits(inetptr) \
	(((inet_struct *) VARDATA_ANY(inetptr))->bits)

#define ip_addr(inetptr) \
	(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)

#define ip_addrsize(inetptr) \
	(ip_family(inetptr) == PGSQL_AF_INET ? 4 : 16)

#define ip_maxbits(inetptr) \
	(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)

#define SET_INET_VARSIZE(dst) \
	SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
				ip_addrsize(dst))


/*
 * 这是 MAC 地址的内部存储格式：
 */
typedef struct macaddr
{
	unsigned char a;
	unsigned char b;
	unsigned char c;
	unsigned char d;
	unsigned char e;
	unsigned char f;
} macaddr;

/*
 * 这是 MAC8 地址的内部存储格式：
 */
typedef struct macaddr8
{
	unsigned char a;
	unsigned char b;
	unsigned char c;
	unsigned char d;
	unsigned char e;
	unsigned char f;
	unsigned char g;
	unsigned char h;
} macaddr8;

/*
 * fmgr 接口宏
 */
#define DatumGetInetPP(X)	((inet *) PG_DETOAST_DATUM_PACKED(X))
#define InetPGetDatum(X)	PointerGetDatum(X)
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
/* 过时的变体 */
#define DatumGetInetP(X)	((inet *) PG_DETOAST_DATUM(X))
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))

/* macaddr 是一种固定长度的按引用传递的数据类型 */
#define DatumGetMacaddrP(X)    ((macaddr *) DatumGetPointer(X))
#define MacaddrPGetDatum(X)    PointerGetDatum(X)
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)

/* macaddr8 是一种固定长度的按引用传递的数据类型 */
#define DatumGetMacaddr8P(X)	((macaddr8 *) DatumGetPointer(X))
#define Macaddr8PGetDatum(X)	PointerGetDatum(X)
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)

/*
 * network.c 中的支持函数
 */
extern inet *cidr_set_masklen_internal(const inet *src, int bits);
extern int	bitncmp(const unsigned char *l, const unsigned char *r, int n);
extern int	bitncommon(const unsigned char *l, const unsigned char *r, int n);

#endif							/* INET_H */
